package com.blankj.utilcode.util;

import android.graphics.Color;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;

import java.util.HashMap;
import java.util.Locale;

/**
 * <pre>
 *     author: Blankj
 *     blog  : http://blankj.com
 *     time  : 2019/01/15
 *     desc  : utils about color
 * </pre>
 */
public final class ColorUtils {

    /**
     * 颜色字符串映射
     */
    private static final HashMap<String, String> sColorNameMap;

    static {
        sColorNameMap = new HashMap<>();
        sColorNameMap.put("black", "#000000");
        sColorNameMap.put("darkgray", "#444444");
        sColorNameMap.put("gray", "#888888");
        sColorNameMap.put("lightgray", "#CCCCCC");
        sColorNameMap.put("white", "#FFFFFF");
        sColorNameMap.put("red", "#FF0000");
        sColorNameMap.put("green", "#00FF00");
        sColorNameMap.put("blue", "#0000FF");
        sColorNameMap.put("yellow", "#FFFF00");
        sColorNameMap.put("cyan", "#00FFFF");
        sColorNameMap.put("magenta", "#FF00FF");
        sColorNameMap.put("aqua", "#00FFFF");
        sColorNameMap.put("fuchsia", "#FF00FF");
        sColorNameMap.put("darkgrey", "#444444");
        sColorNameMap.put("grey", "#888888");
        sColorNameMap.put("lightgrey", "#CCCCCC");
        sColorNameMap.put("lime", "#00FF00");
        sColorNameMap.put("maroon", "#800000");
        sColorNameMap.put("navy", "#000080");
        sColorNameMap.put("olive", "#808000");
        sColorNameMap.put("purple", "#800080");
        sColorNameMap.put("silver", "#C0C0C0");
        sColorNameMap.put("teal", "#008080");
    }

    private ColorUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * Returns a color associated with a particular resource ID.
     *
     * @param id The desired resource identifier.
     * @return a color associated with a particular resource ID
     */
    public static int getColor(@ColorRes int id) {
        return ContextCompat.getColor(Utils.getApp(), id);
    }

    /**
     * Set the alpha component of {@code color} to be {@code alpha}.
     *
     * @param color The color.
     * @param alpha Alpha component \([0..255]\) of the color.
     * @return the {@code color} with {@code alpha} component
     */
    public static int setAlphaComponent(@ColorInt final int color,
                                        @IntRange(from = 0x0, to = 0xFF) int alpha) {
        return (color & 0x00ffffff) | (alpha << 24);
    }

    /**
     * Set the alpha component of {@code color} to be {@code alpha}.
     *
     * @param color The color.
     * @param alpha Alpha component \([0..1]\) of the color.
     * @return the {@code color} with {@code alpha} component
     */
    public static int setAlphaComponent(@ColorInt int color,
                                        @FloatRange(from = 0, to = 1) float alpha) {
        return (color & 0x00ffffff) | ((int) (alpha * 255.0f + 0.5f) << 24);
    }

    /**
     * Set the red component of {@code color} to be {@code red}.
     *
     * @param color The color.
     * @param red   Red component \([0..255]\) of the color.
     * @return the {@code color} with {@code red} component
     */
    public static int setRedComponent(@ColorInt int color,
                                      @IntRange(from = 0x0, to = 0xFF) int red) {
        return (color & 0xff00ffff) | (red << 16);
    }

    /**
     * Set the red component of {@code color} to be {@code red}.
     *
     * @param color The color.
     * @param red   Red component \([0..1]\) of the color.
     * @return the {@code color} with {@code red} component
     */
    public static int setRedComponent(@ColorInt int color,
                                      @FloatRange(from = 0, to = 1) float red) {
        return (color & 0xff00ffff) | ((int) (red * 255.0f + 0.5f) << 16);
    }

    /**
     * Set the green component of {@code color} to be {@code green}.
     *
     * @param color The color.
     * @param green Green component \([0..255]\) of the color.
     * @return the {@code color} with {@code green} component
     */
    public static int setGreenComponent(@ColorInt int color,
                                        @IntRange(from = 0x0, to = 0xFF) int green) {
        return (color & 0xffff00ff) | (green << 8);
    }

    /**
     * Set the green component of {@code color} to be {@code green}.
     *
     * @param color The color.
     * @param green Green component \([0..1]\) of the color.
     * @return the {@code color} with {@code green} component
     */
    public static int setGreenComponent(@ColorInt int color,
                                        @FloatRange(from = 0, to = 1) float green) {
        return (color & 0xffff00ff) | ((int) (green * 255.0f + 0.5f) << 8);
    }

    /**
     * Set the blue component of {@code color} to be {@code blue}.
     *
     * @param color The color.
     * @param blue  Blue component \([0..255]\) of the color.
     * @return the {@code color} with {@code blue} component
     */
    public static int setBlueComponent(@ColorInt int color,
                                       @IntRange(from = 0x0, to = 0xFF) int blue) {
        return (color & 0xffffff00) | blue;
    }

    /**
     * Set the blue component of {@code color} to be {@code blue}.
     *
     * @param color The color.
     * @param blue  Blue component \([0..1]\) of the color.
     * @return the {@code color} with {@code blue} component
     */
    public static int setBlueComponent(@ColorInt int color,
                                       @FloatRange(from = 0, to = 1) float blue) {
        return (color & 0xffffff00) | (int) (blue * 255.0f + 0.5f);
    }

    /**
     * Color-string to color-int.
     * <p>Supported formats are:</p>
     *
     * <ul>
     * <li><code>#RRGGBB</code></li>
     * <li><code>#AARRGGBB</code></li>
     * </ul>
     *
     * <p>The following names are also accepted: <code>red</code>, <code>blue</code>,
     * <code>green</code>, <code>black</code>, <code>white</code>, <code>gray</code>,
     * <code>cyan</code>, <code>magenta</code>, <code>yellow</code>, <code>lightgray</code>,
     * <code>darkgray</code>, <code>grey</code>, <code>lightgrey</code>, <code>darkgrey</code>,
     * <code>aqua</code>, <code>fuchsia</code>, <code>lime</code>, <code>maroon</code>,
     * <code>navy</code>, <code>olive</code>, <code>purple</code>, <code>silver</code>,
     * and <code>teal</code>.</p>
     *
     * @param colorString The color-string.
     * @return color-int
     * @throws IllegalArgumentException The string cannot be parsed.
     */
    public static int stringToInt(@NonNull String colorString) {
        return Color.parseColor(colorString);
    }

    /**
     * Color-int to color-string.
     *
     * @param colorInt The color-int.
     * @return color-string
     */
    public static String intToRgbString(@ColorInt int colorInt) {
        colorInt = colorInt & 0x00ffffff;
        String color = Integer.toHexString(colorInt);
        while (color.length() < 6) {
            color = "0" + color;
        }
        return "#" + color;
    }

    /**
     * Color-int to color-string.
     *
     * @param colorInt The color-int.
     * @return color-string
     */
    public static String intToArgbString(@ColorInt final int colorInt) {
        String color = Integer.toHexString(colorInt);
        while (color.length() < 6) {
            color = "0" + color;
        }
        while (color.length() < 8) {
            color = "f" + color;
        }
        return "#" + color;
    }

    /**
     * Return the random color.
     *
     * @return the random color
     */
    public static int getRandomColor() {
        return getRandomColor(true);
    }

    /**
     * Return the random color.
     *
     * @param supportAlpha True to support alpha, false otherwise.
     * @return the random color
     */
    public static int getRandomColor(final boolean supportAlpha) {
        int high = supportAlpha ? (int) (Math.random() * 0x100) << 24 : 0xFF000000;
        return high | (int) (Math.random() * 0x1000000);
    }

    /**
     * Return whether the color is light.
     *
     * @param color The color.
     * @return {@code true}: yes<br>{@code false}: no
     */
    public static boolean isLightColor(@ColorInt int color) {
        return 0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color) >= 127.5;
    }

    /**
     * 设置View的背景色
     *
     * @param view
     * @param color
     * @param defaultColor
     */
    public static void setBackgroundColor(View view, String color, String defaultColor) {
        try {
            color = wrapColorString(color);
            view.setBackgroundColor(!TextUtils.isEmpty(color) ? Color.parseColor(color) :
                    Color.parseColor(defaultColor));
        } catch (IllegalArgumentException ie) {
        } catch (Exception e) {
        }
    }

    /**
     * 设置View的背景色（带透明度）
     *
     * @param view         view
     * @param opacity      不透明度
     * @param color        颜色字符串
     * @param defaultColor 默认的颜色字符串
     */
    public static void setBackgroundAlphaColor(@NonNull View view, @NonNull float opacity, String color,
                                               String defaultColor) {
        try {
            color = wrapColorString(color);
            view.setBackgroundColor(!TextUtils.isEmpty(color) ? getAlphaColor(opacity, color) : getAlphaColor(opacity
                    , defaultColor));

        } catch (IllegalArgumentException ie) {
        } catch (Exception e) {
        }
    }

    /**
     * 设置文本控件的字体颜色
     *
     * @param textView
     * @param color
     * @param defaultColor
     */
    public static void setTextColor(TextView textView, String color, String defaultColor) {
        try {
            color = wrapColorString(color);
            textView.setTextColor(!TextUtils.isEmpty(color) ? Color.parseColor(color) : Color.parseColor(defaultColor));
        } catch (IllegalArgumentException ie) {
        } catch (Exception e) {
        }
    }

    /**
     * 设置View的背景色
     *
     * @param view
     * @param color
     */
    public static void setBackgroundColor(View view, String color) {
        setBackgroundColor(view, color, "");
    }

    /**
     * 设置文本控件的字体颜色
     *
     * @param textView
     * @param color
     */
    public static void setTextColor(TextView textView, String color) {
        setBackgroundColor(textView, color, "");
    }

    /**
     * 设置文本控件的内容及字体颜色
     *
     * @param textView
     * @param text
     * @param color
     * @param defaultColor
     */
    public static void setTextAndColor(TextView textView, String text, String color, String defaultColor) {
        if (View.VISIBLE != textView.getVisibility()) {
            textView.setVisibility(View.VISIBLE);
        }
        textView.setText(text);
        setTextColor(textView, color, defaultColor);
    }

    /**
     * 设置文本控件的内容及字体颜色
     *
     * @param textView
     * @param text
     * @param color
     */
    public static void setTextAndColor(TextView textView, String text, String color) {
        setTextAndColor(textView, text, color, "");
    }

    /**
     * 获取透明度颜色
     * </p>颜色转换时，如果失败则抛出IllegalArgumentException exception.</p>
     *
     * @param opacity     不透明度
     * @param colorString 颜色字符串
     *                    <p>支持的格式是:</p>
     *                    <ul>
     *                      <li><code>#RRGGBB</code></li>
     *                    </ul>
     *                    <p>允许接收的字符串包括以下: <code>red</code>, <code>blue</code>,
     *                    <code>green</code>, <code>black</code>, <code>white</code>, <code>gray</code>,
     *                    <code>cyan</code>, <code>magenta</code>, <code>yellow</code>, <code>lightgray</code>,
     *                    <code>darkgray</code>, <code>grey</code>, <code>lightgrey</code>, <code>darkgrey</code>,
     *                    <code>aqua</code>, <code>fuchsia</code>, <code>lime</code>, <code>maroon</code>,
     *                    <code>navy</code>, <code>olive</code>, <code>purple</code>, <code>silver</code>,
     *                    and <code>teal</code>.</p>
     * @return 颜色值
     */
    @ColorInt
    public static int getAlphaColor(@NonNull float opacity, @NonNull String colorString) {
        colorString = wrapColorString(colorString);
        if (TextUtils.isEmpty(colorString)) {
            throw new IllegalArgumentException("color is null");
        }
        if (0 > opacity) {
            opacity = 0f;
        }

        if (100 < opacity) {
            opacity = 100f;
        }
        if ('#' != colorString.charAt(0)) {
            colorString = sColorNameMap.get(colorString.toLowerCase(Locale.ROOT));
        }
        if (null == colorString) {
            throw new IllegalArgumentException("color is null");
        }

        float percentageValue = 255 * opacity * 1.0f / 100f;
        String hexStr = Integer.toHexString(Math.round(percentageValue));
        if (hexStr.length() < 2) {
            hexStr = "0" + hexStr;
        }
        return Color.parseColor(colorString.replace("#", "#" + hexStr));
    }

    /**
     * 处理颜色字符串
     *
     * @param color 颜色字符串
     * @return
     */
    public static String wrapColorString(String color) {
        return null == color ? "" : color.replaceAll("\\s*", "");
    }

    /**
     * 设置view可点击状态颜色
     *
     * @param view        view
     * @param enable      是否可点击
     * @param colorString 背景色字符串
     * @param opacity     不可点击时的不透明度(0~100)
     */
    public static void setClickStatusColor(@NonNull View view, Boolean enable, @NonNull String colorString,
                                           @NonNull float opacity) {
        view.setBackgroundColor(getAlphaColor(opacity, colorString));
        view.setEnabled(enable);
    }
}
